package br.org.isvi.interpretador.portugol;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import br.org.isvi.interpretador.portugol.exception.SemanticErrorException;
import br.org.isvi.interpretador.portugol.types.BooleanSymbol;
import br.org.isvi.interpretador.portugol.types.IntegerSymbol;
import br.org.isvi.interpretador.portugol.types.Kind;
import br.org.isvi.interpretador.portugol.types.RealSymbol;
import br.org.isvi.interpretador.portugol.types.StringSymbol;
import br.org.isvi.interpretador.portugol.types.Symbol;
import br.org.isvi.interpretador.portugol.types.Variable;

public class BasicResolver implements Resolver {

	private Logger log;
	private static final String ARQUIVO_LOG = "operacoes.txt";
	private boolean logarAtivo = false;

	HashMap<String, Variable> variables = new HashMap<String, Variable>();

	Symbol[] stack = new Symbol[1024];
	int stackTop = -1;

	public BasicResolver() {
		log = Logger.getLogger(ARQUIVO_LOG);
		try {
			FileHandler fh;
			fh = new FileHandler(ARQUIVO_LOG, true);
			log.addHandler(fh);
			log.setLevel(Level.ALL);
			SimpleFormatter formatter = new SimpleFormatter();
			fh.setFormatter(formatter);
		} catch (Exception e) {
		}
	}

	public void logar(String msg) {
		if (logarAtivo) {
			log.info(msg);
		}
	}

	public void error(String msg) {
		if (logarAtivo) {
			log.severe(msg);
		}
	}

	public void addToStack(Symbol obj) {
		try {
			stackTop++;
			stack[stackTop] = obj;

			logar("adicionou para o stack:" + obj.toString());
		} catch (Exception e) {
			error("Estouro de Pilha!");
		}
	}

	public Symbol popStack() {
		try {
			int last = stackTop;
			stackTop--;

			logar("removeu do stack:" + stack[last].toString());
			return stack[last];
		} catch (Exception e) {
			error("Estouro de Pilha!");
		}

		return null;
	}

	public void declare(Variable obj) {
		if(variables.containsKey(obj.getName()))
			throw new SemanticErrorException("Redeclaração de Variável: " + obj.getName());
		
		variables.put(obj.getName(), obj);
	}

	public void endOPeration(OperationResolver opr) {
		Symbol ret = opr.execute();

		if (ret != null)
			addToStack(ret);
	}

	public void escreva(Symbol valor) {
		System.out.print(valor.toString());
	}

	public void escrevaNL(Symbol valor) {
		escreva(valor);
		System.out.println("");
	}

	public Symbol getOPerationResult() {
		return popStack();
	}

	public void updateTarget(String target) {
		logar("Processando:" + target);
	}

	public Variable getVariable(String name) {
		if (!variables.containsKey(name)) {
			throw new SemanticErrorException("Variavel " + name
					+ " não foi definida!");
		}

		return variables.get(name);
	}

	public void setLogarAtivo(boolean logarAtivo) {
		this.logarAtivo = logarAtivo;
	}

	public void performeHandlingError(String msg, int line, int col) {
		StringBuffer sbf = new StringBuffer();
		sbf.append("ERRO --> ").append(msg);

		if (line >= 0) {
			sbf.append(" na linha: ").append(line).append(" e coluna: ")
					.append(col);
		}

		System.out.println(sbf.toString());
	}

	public int stackSize() {
		return stackTop;
	}

	public Symbol leia(Kind k) {
		Symbol ret = null;
		BufferedReader b = new BufferedReader(new InputStreamReader(System.in));

		String value = null;
		try {
			value = b.readLine();

			switch (k) {
			case TEXTO:
				ret = new StringSymbol(value);
				break;
			case INTEIRO:
				ret = IntegerSymbol.valueOf(value);
				break;
			case BOOLEANO:
				ret = BooleanSymbol.valueOf(value);
				break;
			case REAL:
				ret = RealSymbol.valueOf(value);
				break;
			default:
				break;
			}
		} catch (IOException e) {
			throw new SemanticErrorException("Erro de leitura! Não foi possivel ler a partir do buffer de entrada!");
		}

		return ret;
	}

	public void printStack() {
		
		for(int i = 0; i <= stackTop; i++)
			System.out.println(stack[i]);
	}

	public Variable setVariable(Variable var, Symbol sym) {
		Variable v = getVariable(var.getName());
		v.setValue(sym);
		
		return v;
	}

}
